
#include "items.hpp"
#include "common.hpp"

#include <QtWiTmpl>


/////////////
// Globals //
/////////////

StationType station_type;


// CPUusage

int CPUusage::utilization() const
{
	static const std::string cpul = "cpu";
	std::string line;

	std::ifstream cpuinfo(cpu_proc, std::ios_base::in);

	if(!cpuinfo.is_open()) {

#ifdef DEBUG
		std::cerr << "Unable to open for reading: " << CPUusage::cpu_proc << std::endl;
#endif
		return -1;
	}
		
	while(!cpuinfo.eof()) {
		getline(cpuinfo, line);
		if(!line.compare(0, cpul.length(), cpul))
			break;
	}

	std::vector<std::string> t = tokenize(line, ' ');
	std::vector<std::string>::iterator i = t.begin();

	int total_time = 0;
		
	for( ++i; i != t.end(); i++)
		total_time += string_convert<int>(*i);

	static int prev_idle;
	static int prev_total;
		
	int idle_time = string_convert<int>(t[4]);
	int idle_diff = idle_time - prev_idle;
	int total_diff = total_time - prev_total;	
	
	prev_idle = idle_time;
	prev_total = total_time;
		
	return (1000 * (total_diff - idle_diff) / total_diff) / 10 ;
}


// MEMusage

int MEMusage::utilization() const
{

#define getKb(VAR, STR) \
	VAR = string_convert<int>(line.substr(STR.length(), line.rfind("kB") - STR.length()));
	
	std::string memTot;
	std::string memFree; 

	if(type == Memory) {
		memTot = "MemTotal:";
		memFree = "MemFree:";
	} else {
		memTot = "SwapTotal:";
		memFree = "SwapFree:";
	}
	
	std::ifstream meminfo(mem_proc, std::ios_base::in);

	if(!meminfo.is_open()) {

#ifdef DEBUG
		std::cerr << "Unable to open for reading: " << MEMusage::mem_proc << std::endl;
#endif
		return -1;
	}

	int mem_total = -1, mem_free = -1;
	std::string line;
	
	while(!meminfo.eof()) {
		
		getline(meminfo, line);

		if(!line.compare(0, memTot.length(), memTot)) {
			getKb(mem_total, memTot);
		}

		if(!line.compare(0, memFree.length(), memFree)) {
			getKb(mem_free, memFree);
			break;
		}
	}

	if(mem_total == -1 || mem_free == -1) {

#ifdef DEBUG
		std::cerr << "Unable to obtain " << ((type == Memory) ? std::string("memory") : std::string("swap")) << " information" << std::endl;
#endif
		return -1;
	}
		
	return ((mem_total - mem_free) * 100) / (mem_total ? mem_total : 1);
}

// GuiConnector
GuiConnector::GuiConnector(IWidget &parent, Ifactory &f, int x, int y, int w, int h) :
	IWidgetWrapper<IRawWidget>(parent, f, x, y, w, h),
	net_panel(*widget(), f, gui_connector_height),
	file_panel(*widget(), f, gui_connector_height),
	set_panel(*widget(), f, this, gui_connector_height),
	connector(NULL), connector_lock(connector),
	lbls(*widget(), f, "Chunk:", general_widget_buffer, connect_panels_offsety,
	     main_widgets_width - general_widget_buffer, 30, 0, Connector::max_chunk_size, 100, 100, true),
	lbls_lock(lbls),
	slider_delay(*widget(), f, "Delay:", general_widget_buffer, connect_panels_offsety + 24,
	     main_widgets_width - general_widget_buffer, 30, 0, Connector::max_delay_time, 100, 100, true),
	slider_delay_lock(slider_delay),
	lblb(*widget(), f, "Rate:", general_widget_buffer, rate_label_offsety, main_widgets_width * 0.33, 30),
	lbltot(*widget(), f, "Total:", general_widget_buffer, rate_label_offsety + general_widget_height + 2.2 * general_widget_buffer,
	       main_widgets_width * 0.33, 30),
	timing(f, *widget(), general_widget_buffer + 230, rate_label_offsety + general_widget_height + 2 * general_widget_buffer,
	       100, general_widget_height, 1),
	rate_logger("rate"),
	total_logger("total"),
	time_logger("time"),
	chunk_logger("chunk"),
	delay_logger("delay"),
	loadavg_logger("loadavg"),
	s11n_logger("s11n")
{
	source_panel = &net_panel;
	destination_panel = &file_panel;

	source_gauge = sink_gauge = NULL;
		
	source_gauge_chk = ftory().makeCheckButton(widget(), "Source Gauge", general_widget_buffer + 230, rate_label_offsety);
	source_gauge_chk->show();
	sink_gauge_chk = ftory().makeCheckButton(widget(), "Sink Gauge", general_widget_buffer + 130,  rate_label_offsety);
	sink_gauge_chk->show();

	ftory().getApp().registerAction(*source_gauge_chk, IAction::CLICK, this);
	ftory().getApp().registerAction(*sink_gauge_chk, IAction::CLICK, this);
		
	set_panel.widget()->move(2 * (general_widget_buffer + destination_panel_offsetx), 0);

	lbls.setCurrent(Connector::default_chunk_size);
		
	refresh();
		
	// Register main timer
	ftory().getApp().registerTimer(this, widget(), update_interval);

	station_type = RECEIVER;
	write_logfiles = true;

	write_logs_chk = ftory().makeCheckButton(widget(), "Write Logs", general_widget_buffer + 130, rate_label_offsety + general_widget_height +
						 2 * general_widget_buffer);

	write_logs_chk->setChecked(true);

	ftory().getApp().registerAction(*write_logs_chk, IAction::CLICK, this);
}

vecstr GuiConnector::tokenize_ip_port_str()
{
	
	std::vector<std::string> n = tokenize(net_panel.boxText(), ':');
					
	if(n.size() < 1 || n.size() > 2) {
		ftory().getApp().affiliateWidget(*new IStandardMonologue(ftory(), "IP/Port Field", 30, 30,
									 "Network text field must be of the form:\n<IP>:<PORT>"));
		return vecstr();
	}

	return n;
}

void GuiConnector::connect()
{

	if(source_panel->id == ConnectorPanel::Network) {
		
		switch(source_panel->selected()) {

			// Select source for incoming data fd
			case NetworkPanel::TCP:
				{
					// TCP server
					vecstr n = tokenize_ip_port_str();

					if(n[0] != "127.0.0.1") {

						ftory().getApp().affiliateWidget(*new IStandardMonologue(ftory(),"IP Address",
													 30, 30, "IP must be 127.0.0.1 for server."));

						return;
					}

					source = new TCPsource(string_convert<uint16_t>(n[1]));

					// todo: height constants
					source_gauge = new TCPgauge(ftory(), "TCP Gauge", 30, 20, 370);
					
				}
				
				break;
						
			case NetworkPanel::UART:
				source = new SerialSource(net_panel.boxText());
				source_gauge = new SerialGauge(ftory(), "Serial Gauge", 30, 20, 360);		
				break;
			case NetworkPanel::CAN:
				;
				break;
		}

		switch(destination_panel->selected()) {

			case FilePanel::FILE:
			case FilePanel::RANDOM:
				sink = new FileDestination(file_panel.boxText());
				sink_gauge = new FileGauge(ftory(), "File Gauge", 30, 20, 170);
				break;
			case FilePanel::PIPE:
				sink = new PipeDestination(file_panel.boxText());
				sink_gauge = NULL;	
				break;
		}

		// Select sink for outgoing data fd	
	} else {

		switch(source_panel->selected()) {

			case FilePanel::FILE:
			case FilePanel::RANDOM:	
				source = new FileSource(file_panel.boxText());
				source_gauge = new FileGauge(ftory(), "File Gauge", 30, 20, 170);
				break;

			case FilePanel::PIPE:
				source = new PipeSource(file_panel.boxText());
				source_gauge = NULL;
				break;

		}

		switch(destination_panel->selected()) {

			case  NetworkPanel::TCP:
				{
					vecstr n = tokenize_ip_port_str();
					sink = new TCPdestination(n[0].c_str(), string_convert<uint16_t>(n[1]));
					sink_gauge = new TCPgauge(ftory(), "TCP Gauge", 30, 20, 370);

				}
				break;
			case NetworkPanel::UART:
				sink = new SerialDestination(net_panel.boxText());
				sink_gauge = new SerialGauge(ftory(), "Serial Gauge", 30, 20, 360);
				break;
			case NetworkPanel::CAN:
				break;
		}
	}

	// Initial set up of source and sink
	source->getReady();

	if(source->hasErrors()) {

#ifdef DEBUG		
		std::cerr << "Source error: " << source->errorMsg() << std::endl;
#endif
		ftory().getApp().affiliateWidget(*new IStandardMonologue(ftory(), "Source Error", 30, 30, source->errorMsg()));
		goto errors;
	}

	sink->getReady();

	if(sink->hasErrors()) {
		
#ifdef DEBUG
		std::cerr << "Destination error: " << sink->errorMsg()
			  << std::endl;
#endif
		ftory().getApp().affiliateWidget(*new IStandardMonologue(ftory(), "Sink Error", 30, 30, sink->errorMsg()));
		
		goto errors;
	}

	
#ifdef DEBUG
	std::cerr << "Source status: " << source->statusMsg() << std::endl;
	std::cerr << "Sink status: " << sink->statusMsg() << std::endl;
#endif

	app_console_lock.aquire();
	*app_console << Console::note << source->statusMsg() << Console::endl;
	*app_console << Console::note << sink->statusMsg() << Console::endl;
	app_console_lock.release();

	// Connect source and sink and start data transfer
	connector_lock.set(new Connector(source, sink));
	connector_lock.read()->start();
	
	// todo: remove connector -> read, delete, write NULL

	usleep((messy_concurrency_fixup << 3));
	
	sink_gauge_chk->setChecked(true);
	source_gauge_chk->setChecked(true);

	return;

 errors:
	app_console_lock.aquire();
	*app_console << Console::note << "Errors occurred" << Console::endl;
	app_console_lock.release();
	usleep(messy_concurrency_fixup);
	set_panel.establish_button_setup(true);
	return;
}

void GuiConnector::disconnect()
{
	connector_lock.aquire();
	
	connector->stop();

	// TODO:
	//delete connector;
	//delete sinks and sources (in connector?)
	//delete gauges
	
	connector_lock.unchecked_st(NULL);
	timing.stop();
	
	connector_lock.release();
}

void GuiConnector::establish()
{
	static bool cnt = false;
	
	if(!connected && set_panel.caption() == std::string(SettingsPanel::establ_caption)) {
		cnt = connected = true;
		connect();
		set_panel.establish_button_setup(false);
	} else if(connected) {
		cnt = connected = false;
		disconnect();
		set_panel.establish_button_setup(true);
		lblb.textbox()->setText("0 bit/s");
	}
	
}

// Handle "Establish" button and Receiver/Sender radio button click actions
void GuiConnector::handler(IWidget &snd)
{
	
	// Radio buttons toggled
	if(qtwi_rtti_cmp(snd, IRadioButton)) {

		const char *c = dynamic_cast<IRadioButton&>(snd).getCaption();
			
		// Fixme: Make "Sender" string class constant
		if(!strncmp(c, lenof("Sender"))) {
			source_panel = &file_panel;
			destination_panel = &net_panel;
			station_type = SENDER;
		} else {
			source_panel = &net_panel;
			destination_panel = &file_panel;
			station_type = RECEIVER;
		}

		refresh();

	}

	// Establish button toggled
	else if(qtwi_rtti_cmp(snd, IButton)) {
		// Fixme: Synchronization bug; somehow for TNW the button event is sent two times
		usleep(messy_concurrency_fixup << 3);
		establish();
	}

	// Check buttons for gauge windows
	else if(qtwi_rtti_cmp(snd, ICheckButton)) {

		ICheckButton &cbtn = dynamic_cast<ICheckButton&>(snd);

		if(!strcmp(cbtn.getCaption().c_str(), "Source Gauge")) {

			if(!source_gauge)
				cbtn.setChecked(false);
			else {
				
				if(cbtn.checked())
					source_gauge->visible();
				else
					source_gauge->invisible();
			}
					
		} else if (!strcmp(cbtn.getCaption().c_str(), "Sink Gauge")) {
				
			if(!sink_gauge)
				cbtn.setChecked(false);
			else {
				
				if(cbtn.checked())
					sink_gauge->visible();
				else
					sink_gauge->invisible();
			}
				
		} else {
				
			write_logfiles = write_logs_chk->checked();
		}
	}
		
}

void GuiConnector::timeout()
{
	static long double prev_rate;
	long double rate;
	static int timeout_counter;
	static int status_query_interval_counter;
	static int prev_chunk_size;
	static int prev_max_delay;
	static int stalled_counter;
	
	connector_lock.aquire();
	
	if(!connector) {
#ifdef DEBUG
		//std::cout << "connector == null\n";
#endif
		goto leave;
	}

	if(!connector->isRunning()) {
#ifdef DEBUG
		std::cout << "connector stopped\n";
#endif		
		connector_lock.release();
		disconnect();
		return;
	}
		
	// todo: delete connector and source, destroy source and sink in destructor of Connector -> segfaults

	if(connector->hasErrors()) {
		// todo: Monologue
		std::cerr << "connector error: " << connector->errorMsg()
			  << std::endl;
		connector->stop();
	}
	
	rate = connector->getDataRate();

	if(connector->isPumping()) {
		timing.run();
		rate_logger.out() << rate << std::endl;
		total_logger.out() << connector->getTotalBytes() << std::endl;
		chunk_logger.out() << lbls.current() << std::endl;
		delay_logger.out() << slider_delay.current() << std::endl;
	}

	// Stalled after 3 seconds
	if(stalled_counter > 3 && rate > 0) {
		lblb.textbox()->setText("stalled");
		timeout_counter += update_interval;

		if(prev_rate != rate)
			stalled_counter = 0;
	}
	else {
		timeout_counter = 0;
		lblb.textbox()->setText(const_cast<char*>(Connector::bitRateString(rate).c_str()));

		if(prev_rate == rate)
			stalled_counter++;
	}
	
	lblb.textbox()->refresh();
	
	prev_rate = rate;

	if(timeout_counter >= Connector::connection_timeout) {

		// TODO
		/*app_console_lock.aquire();
		*app_console << Console::note << "Connection timed out ..." << Console::endl;
		app_console_lock.release();
		
		connector_lock.release();
		disconnect();
		set_panel.establish_button_setup(true);
		lblb.textbox()->setText("0 bit/s");
		
		return;
		*/
	}

	// Set total bytes transferred
	lbltot.textbox()->setText((Connector::unit_convert(connector->getTotalBytes(), true) + "s").c_str());

	app_console_lock.aquire();
	
	status_query_interval_counter += 1;//update_interval;
	
	// Query Connector, Source, and Destination for their stati
	if(status_query_interval_counter >= 0) { //status_query_interval) {
		
#define status_print(EREPORT) \
		status = EREPORT->getStatus();\
		if(status.size() > 1) {\
			*app_console << Console::note << status << Console::endl; \
		}

		std::string status;
		status_print(connector);
		status_print(source);
		status_print(sink);

		status_query_interval_counter = 0;
	}

	// Reset chunk and max delay size according to gui slider
	if(prev_chunk_size != lbls.current()) {
		connector->setChunkSize(lbls.current());
		*app_console << Console::note << "New chunk size: " << lbls.current() << " bytes" << Console::endl; \
		prev_chunk_size = lbls.current();
	}

	if(prev_max_delay != slider_delay.current()) {
		connector->setMaxDelay(slider_delay.current());
		*app_console << Console::note << "New max delay: " << slider_delay.current() << " ms" << Console::endl; \
		prev_max_delay = slider_delay.current();
	}

	app_console_lock.release();

	// Read values from Source and Sink and feed them to the specific gauges
	usleep(messy_concurrency_fixup);

	if(source_gauge) {
		
		source_gauge->setRate(rate);
		source_gauge->setChunk(lbls.current());
		GaugeInfo &sinf = source->getGaugeInfo();
		source_gauge->feedGaugeInfo(&sinf);
	}

	if(sink_gauge) {
		sink_gauge->setRate(rate);
		sink_gauge->setChunk(lbls.current());
		GaugeInfo &sinf = sink->getGaugeInfo();
		sink_gauge->feedGaugeInfo(&sinf);

	}

	// Log load average
	static const char *lavg = "/proc/loadavg";
	float laverage;	
	FILE *f;
	
	if((f = fopen(lavg, "r")) &&
	    fscanf(f, "%f", &laverage) == 1) {

		loadavg_logger.out() << laverage << std::endl;
		fclose(f);
	}

	//Serialization
	s11n_logger.out() << connector->getS11n() << std::endl;

#ifndef ITEMS_HPP
#define ITEMS_HPP

#include "common.hpp"
#include "transfer.hpp"
#include "gauges.hpp"

#include <QtWi>
#include <QtWiTmpl>

#include <fstream>
#include <vector>
#include <iostream>
#include <string>

#include <typeinfo>


////////////////////
// Status console //
////////////////////

class Console : public ITemplate
{
private:
	ITextBox *tb;
public:

	enum Symbols { note, warning, error, endl };
	
	Console(IWidget &parent, Ifactory &f, const int &x, const int &y,
		const int &w, const int &h) : ITemplate(parent, f)
	{
		tb = ftory().makeTextBox(&parent, "", x, y, ITextBox::Multi);
		tb->resize(w, h);
		tb->show();
	}

	Console& operator<<(const int &i)
	{
		tb->appendText(const_cast<char*>(to_string<int>(i).c_str()));
		return *this;
	}

	Console& operator<<(const char *s)
	{
		tb->appendText(s);
		return *this;
	}

	Console& operator<<(std::string s)
	{
		tb->appendText(const_cast<char*>(s.c_str()));
		return *this;
	}

	Console& operator<<(Console::Symbols s)
	{
		switch(s) {
			case endl:
				tb->appendText("\n");
				break;
			case note:
				tb->appendText("* ");
				break;
			case warning:
				tb->appendText("* WARNING: ");
				break;
			case error:
				tb->appendText("* ERROR: ");
				break;
		}
		return *this;
	}

};

////////////////
// Usage bars //
////////////////

class UsageBar : public ITimerHandler
{
protected:
	IProgressBar *bar;
	ILabel *lbl;
	int current;
	ValueLogger log;

private:
	virtual int utilization() const = 0;

public:
	static const int label_width;
	static const int usg_bar_height;
	
	UsageBar(Ifactory &f, IWidget &p, const char *caption, const int &x, const int &y,
		 const int &len, const char *logname) : ITimerHandler(bar), log(logname)
	{
		const int all_h = general_widget_height;
		lbl = f.makeLabel(&p, caption, x, y);
		lbl->resize(label_width, all_h);
		bar = f.makeProgressBar(&p, "", x + label_width + 5, y, 0, 100, 1);
		bar->resize(len - label_width, all_h);
		bar->setCurrent(0);
	}

	void timeout()
	{
		int usg;

		if((usg = utilization()) < 0) 
			return;

		current = usg;
		bar->setCurrent(usg);
		bar->refresh();
		if(connected) log.out() << current << std::endl;
	}

	IProgressBar* getBar() {
		return bar;
	}

};


class CPUusage : public UsageBar
{
	
public:
	static const int update_interval;
	static const char *cpu_proc;

private:
	int utilization() const;
	
public:
	
	CPUusage(Ifactory& f, IWidget &p, const char *caption, const int &x, const int &y,
		 const int &len, const char *logname) :
		UsageBar(f, p, caption, x, y, len, logname) { }
};


class MEMusage : public UsageBar
{
public:
	static const int update_interval;
	static const char *mem_proc;

	const enum Type
	{
		Memory,
		Swap
	} type;
	
private:
	int utilization() const;

public:
	MEMusage(Ifactory& f, IWidget &p, const char *caption,
		 MEMusage::Type t, const int &x, const int &y, const int &len, const char *logname) :
		UsageBar(f, p, caption, x, y, len, logname), type(t) { }
};

class UsageBars : public IWidgetWrapper<IRawWidget>
{
private:
	CPUusage cpu;
	MEMusage mem;
	MEMusage swap;
public:
	UsageBars(IWidget &parent, Ifactory &fact, const int &x, const int &y,
		  const int &w, const int &h) :
		IWidgetWrapper<IRawWidget>(parent, fact, x, y, w, h),
		cpu(CPUusage(ftory(), *widget(), "CPU:", 0, UsageBar::usg_bar_height, w, "cpu")),
		mem(MEMusage(ftory(), *widget(), "Mem:", MEMusage::Memory, 0,
			     2 * UsageBar::usg_bar_height, w, "mem")),
		swap(MEMusage(ftory(), *widget(), "Swap:", MEMusage::Swap, 0,
			      3 * UsageBar::usg_bar_height, w, "swap"))
	{
		ftory().getApp().registerTimer(&cpu, cpu.getBar(), CPUusage::update_interval);
		ftory().getApp().registerTimer(&mem, mem.getBar(), MEMusage::update_interval);
		ftory().getApp().registerTimer(&swap, swap.getBar(), MEMusage::update_interval);
	}
};


///////////////
// Connector //
///////////////


// Panels

class ConnectorPanel : public IWidgetWrapper<IRadioGroup>, protected IActionHandler
{
	typedef std::vector<IRadioButton*> btn_vec;
	
private:
	btn_vec *buttons;
	ITextBox *tb;
	int current_y;

public:
	// Mock rtti; neccessary for GuiConnector
	enum {
		Network = 0,
		File,
	};

	const int id;
	
public:

	// todo: destructors, remove vector
	// todo: removeButton and deconstruct/delete all buttons in vector
	
	static const int connector_panel_width;
	static const int content_offsetx;
	
	ConnectorPanel(IWidget &parent, Ifactory &fact, int height, int cid) :
		IWidgetWrapper<IRadioGroup>(parent, fact, 0, 0, connector_panel_width, height),
		buttons(new btn_vec), current_y(0), id(cid)
	{
		tb = ftory().makeTextBox(widget(), "", content_offsetx, current_y, ITextBox::Single);
		tb->resize(connector_panel_width - 2 * content_offsetx, general_textbox_height);
	}
	
	int selected() const
	{
		int idx = 0;
	
		for(btn_vec::const_iterator i = buttons->begin(); i != buttons->end(); i++, idx++) {
			if((*i)->checked())
				return idx;
		}

		return -1;
	}

	ITextBox* textbox() const
	{
		return tb;
	}

	std::string boxText() const
	{
		return tb->text();
	}


	void call_handler()
	{
		handler(*tb);
	}
	
protected:
	void addRadioButton(const char *caption, bool checked)
	{
		current_y += general_widget_height + general_widget_buffer / 2;
		
		IRadioButton *b = ftory().makeRadioButton(widget(), caption,
							  content_offsetx, current_y);
		
		b->setChecked(checked);
		buttons->push_back(b);
		
		tb->move(content_offsetx, current_y + general_widget_height +
			 general_widget_buffer * 2);

		ftory().getApp().registerAction(*b, IAction::CLICK, this);
	}

};

class NetworkPanel : public ConnectorPanel
{

public:

	// Radio button indices
	enum {
		TCP = 0,
		UART,
		CAN
	};
	
	NetworkPanel(IWidget &parent, Ifactory &fact, int height) :
		ConnectorPanel(parent, fact, height, ConnectorPanel::Network)
	{
		
		addRadioButton("TCP", true);
		addRadioButton("Serial", false);
		addRadioButton("CAN", false);
	}

public:

	// Default texts
	void handler(IWidget &w)
	{
		std::string dtxt;
		
		switch(selected()) {

			case TCP:
				sndr_recv(
#ifdef SNDIP
					  SNDIP
#else					  
					  "127.0.0.1:6666"
#endif
					  ,
#ifdef RECIP
					  RECIP
#else
					  "127.0.0.1:6666"
#endif
					  );
				break;

			case UART:
				sndr_recv("/dev/ttyS0", "/dev/ttyS0");
				break;

			case CAN:
				sndr_recv("can0", "can1");
				break;		
		}

		textbox()->setText(const_cast<char*>(dtxt.c_str()));
		
	}
	
};

class FilePanel : public ConnectorPanel
{
public:

	enum {
		FILE = 0,
		PIPE,
		RANDOM
	};
	
	FilePanel(IWidget &parent, Ifactory &fact, int height) :
		ConnectorPanel(parent, fact, height, ConnectorPanel::File)
	{
		addRadioButton("File", false);
		addRadioButton("Pipe", true);
		addRadioButton("Random", false);

	}

	// Default texts
	void handler(IWidget &w)
	{
		std::string dtxt;
		
		switch(selected()) {

			case FILE:
				sndr_recv("/dev/zero", "/tmp/ufile");
				break;

			case PIPE:
				sndr_recv("/tmp/ubench/fifo", "/tmp/ubench/fifo");
				break;

			case RANDOM:
				sndr_recv("/dev/random", "/dev/null");
				break;
		}

		textbox()->setText(const_cast<char*>(dtxt.c_str()));
	}
};

class SettingsPanel : public IWidgetWrapper<IRadioGroup>
{

private:
	IRadioButton *send;
	IRadioButton *recv;
	IButton *establ;
	
public:

	static const char *establ_caption;
	static const char *cancel_caption;
	
	void establish_button_setup(bool connectable)
	{
		if(connectable)
			establ->setCaption(establ_caption);
		else
			establ->setCaption(cancel_caption);
	}

	std::string caption() const
	{
		return establ->caption(); 
	}

	// todo: action handler
	SettingsPanel(IWidget &parent, Ifactory &fact, IActionHandler *gui_connector, int height) :
		IWidgetWrapper<IRadioGroup>(parent, fact, 0, 0,
					    ConnectorPanel::connector_panel_width, height)
	{
		widget()->setCaption("Settings");

		send = ftory().makeRadioButton(widget(), "Sender", ConnectorPanel::content_offsetx,
					       general_widget_height);
		
		recv = ftory().makeRadioButton(widget(), "Receiver", ConnectorPanel::content_offsetx,
					       general_widget_height * 2 + general_widget_buffer);

		establ = ftory().makeButton(widget(), "", ConnectorPanel::content_offsetx,
					     general_widget_height * 4 + general_widget_buffer);
		
		recv->setChecked(true);
		establ->resize(ConnectorPanel::connector_panel_width -
			       2 * ConnectorPanel::content_offsetx, general_button_height);

		recv->show();
		send->show();
		establish_button_setup(true);
		establ->show();

		ftory().getApp().registerAction(*send, IAction::CLICK, gui_connector);
		ftory().getApp().registerAction(*recv, IAction::CLICK, gui_connector);
		ftory().getApp().registerAction(*establ, IAction::CLICK, gui_connector);
	}
};

class GuiConnector : public IWidgetWrapper<IRawWidget>, public IActionHandler, public ITimerHandler
{
private:

	// Connector
	
	ConnectorPanel *source_panel;
	ConnectorPanel *destination_panel;

	NetworkPanel net_panel;
	FilePanel file_panel;
	SettingsPanel set_panel;

	DataSource *source;
	DataDestination *sink;

	Connector *connector;
	IMutexLock<Connector*> connector_lock;

	// Chunk size slider
	ILabeledSlider lbls;
	IMutexLock<ILabeledSlider> lbls_lock;

	// Max delay slider
	ILabeledSlider slider_delay;
	IMutexLock<ILabeledSlider> slider_delay_lock;

	// Bit rate label
	ILabeledTextBox lblb;

	// Total transferred label
	ILabeledTextBox lbltot;

	// Gauges
	Gauge *source_gauge;
	Gauge *sink_gauge;

	ICheckButton *sink_gauge_chk;
	ICheckButton *source_gauge_chk;
	ICheckButton *write_logs_chk;
	Timing timing;

	// Log files
	ValueLogger rate_logger;
	ValueLogger total_logger;
	ValueLogger time_logger;
	ValueLogger chunk_logger;
	ValueLogger delay_logger;
	ValueLogger loadavg_logger;
	
private:
	
	void establish();
	void connect();
	void disconnect();

	vecstr tokenize_ip_port_str();
	
public:
	static const int gui_connector_overall_height;
	static const int destination_panel_offsetx;
	static const int gui_connector_height;
	static const int connect_panels_offsety;
	
	static const int gauge_control_offsety;
	static const int gauge_control_height;

	static const int rate_label_offsety;
	
	static const int update_interval;
	static const int status_query_interval;
	
	GuiConnector(IWidget &parent, Ifactory &f, int x, int y, int w, int h);

	void refresh();

	virtual void handler(IWidget &snd);
	
	virtual void timeout();

};


#endif

	
 leave:
	connector_lock.release();
}


void GuiConnector::refresh()
{
	source_panel->widget()->move(0, 0);
	destination_panel->widget()->move(destination_panel_offsetx, 0);

	source_panel->widget()->setCaption("Source");
	destination_panel->widget()->setCaption("Sink");
		
	source_panel->widget()->refresh();
	destination_panel->widget()->refresh();

	source_panel->call_handler();
	destination_panel->call_handler();
}
